﻿/**
 *    Copyright 2012 Yohan Courbe
 *
 *  Licensed under the Apache License, Version 2.0 (the "License 
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *      
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using LukeSkywalker.IPNetwork;
using NetworkAnalyzer.Engines;
using NetworkAnalyzer.Engines.Graphics;
using NetworkAnalyzer.Engines.HttpTracker;
using NetworkAnalyzer.Engines.Sonification;
using NetworkAnalyzer.Engines.Statistics;
using NetworkAnalyzer.Engines.TcpSessionTracker;
using NetworkAnalyzer.Engines.UdpTransfertTracker;
using PacketDotNet;
using SharpPcap;

namespace NetworkAnalyzer
{
    public sealed class NetworkAnalyzer
    {
        private static readonly EnginesManager EnginesManager = new EnginesManager();
        private volatile bool _shouldStopThread = false;
        private static List<RawCapture> _packetsQueue = new List<RawCapture>();
        private static readonly object QueueLock = new object();
        private static Thread _networkAnalyzerThread;
        private static ICaptureDevice _captureDevice;

        public static int PacketCaptured = 0;
        public static int PacketAnalyzed = 0;
        public static IPNetwork IpNetwork { get; private set; }

        public NetworkAnalyzer()
        {
            EnginesManager.RegisterEngine(new StatisticsEngine(), "Statistics");
            //EnginesManager.RegisterEngine(new SonifictionEngine(), "Sonification");
            EnginesManager.RegisterEngine(new TcpSessionTrackerEngine(), "TcpSessionTracker");
            EnginesManager.RegisterEngine(new UdpTransfertTrackerEngine(), "UdpTransfertTracker");
            EnginesManager.RegisterEngine(new HttpTrackerEngine(), "HttpTracker");
            EnginesManager.RegisterEngine(new GraphicsEngine(), "Graphics");
        }

        private void NetworkAnalyserThread()
        {
            while (!_shouldStopThread)
            {
                List<RawCapture> localPacketsQueue;

                lock (QueueLock)
                {
                    localPacketsQueue = _packetsQueue;
                    _packetsQueue = new List<RawCapture>();
                }

                try
                {
                    var start = DateTime.Now;

                    foreach (var packet in localPacketsQueue.Select(packetRaw => Packet.ParsePacket(packetRaw.LinkLayerType, packetRaw.Data)))
                    {
                        EnginesManager.DispatchPacket(packet);
                        PacketAnalyzed++;
                    }

                    var end = DateTime.Now - start;
                    //Console.WriteLine("Queue size: {2} Time: {0} s {1} ms", end.Seconds, end.Milliseconds, localPacketsQueue.Count);
                }
                catch (Exception e)
                {
                    Console.WriteLine("EXCEPTION {0}", e.StackTrace);
                }
            }

            Console.WriteLine("NetworkAnalyzerThread has terminated");
        }

        public void Start(ICaptureDevice device, string cidr)
        {
            IpNetwork = IPNetwork.Parse(cidr);

            Console.WriteLine("start NetworkAnalyzerThread");

            _captureDevice = device;

            _captureDevice.OnPacketArrival += new PacketArrivalEventHandler(OnPacketArrival);
            _captureDevice.Open(DeviceMode.Promiscuous);
            _captureDevice.StartCapture();

            _networkAnalyzerThread = new Thread(new ThreadStart(NetworkAnalyserThread));
            _networkAnalyzerThread.Start();
        }

        public void Stop()
        {
            Console.WriteLine("stop NetworkAnalyzerThread");
            _shouldStopThread = true;
            _networkAnalyzerThread.Join();

            _captureDevice.StopCapture();
            _captureDevice.Close();
        }

        private static void OnPacketArrival(object sender, CaptureEventArgs e)
        {
            lock (QueueLock)
            {
                //Console.WriteLine("Packet captured");
                _packetsQueue.Add(e.Packet);
                PacketCaptured++;
            }
        }
    }
}
